package com.eastfair.security.component;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ClassUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.eastfair.cache.redis.RedisOps;
import com.eastfair.core.base.R;
import com.eastfair.core.businmodel.TenantIgnoreTable;
import com.eastfair.core.context.ContextConstants;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.context.StaticContextUtil;
import com.eastfair.core.dynamic.DynamicAnno;
import com.eastfair.core.dynamic.FieldTypeUtil;
import com.eastfair.core.model.DataIsolationModel;
import com.eastfair.core.model.DataScopeEnum;
import com.eastfair.core.model.SystemTagEnum;
import com.eastfair.core.rocketmq.RocketTopicData;
import com.eastfair.core.utils.NetUtils;
import com.eastfair.dynamic.DynamicPackage;
import com.eastfair.model.DataIsolationVo;
import com.eastfair.model.TenantRuleVO;
import com.eastfair.plumelog.handler.MessageAppenderFactory;
import com.eastfair.projectcore.api.DictionaryFeign;
import com.eastfair.projectcore.api.ProjectCoreServiceFeign;
import com.eastfair.projectcore.dto.DataIsolationDTO;
import com.eastfair.projectcore.entity.WorkerNode;
import com.eastfair.rocketmq.RocketTopicDataOfBusiness;
import com.eastfair.security.distionarydynamic.DistionaryDynamicAnno;
import com.eastfair.security.handler.DataIsolactionHandler;
import com.eastfair.uid.baidu.impl.CachedUidGenerator;
import com.eastfair.uid.baidu.model.WorkerNodeOfUid;
import com.eastfair.uid.baidu.rule.SnowFlakeRole;
import com.eastfair.uid.baidu.utils.DockerUtils;
import com.eastfair.uid.baidu.worker.WorkerNodeType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

import static com.eastfair.core.base.R.SUCCESS_CODE;

/**
 * springboot容器启动完成后运行
 */
@Component
@Slf4j
public class ApplicationRunnerCore implements ApplicationRunner {
    @Autowired
    ProjectCoreServiceFeign projectCoreServiceFeign;


    private static final Random RANDOM = new Random();
    @Autowired
    DictionaryFeign dictionaryFeign;

    @Autowired
    RedisOps redisOps;

    @Autowired
    DistionaryDynamicAnno distionaryDynamicAnno;

    @Value("#{'${tenant-table.base-untenant:}'.empty ? null : '${tenant-table.base-untenant:}'.split(' ')}")
    private List<String> baseUntenant;


    @Value("#{'${tenant-table.baseSystemOrTeamTable:}'.empty ? null : '${tenant-table.baseSystemOrTeamTable:}'.split(' ')}")
    private List<String> baseSystemOrTeamTable;


    @Value("#{'${tenant-table.baseSystemTable:}'.empty ? null : '${tenant-table.baseSystemTable:}'.split(' ')}")
    private List<String> baseSystemTable;


    @Value("#{'${tenant-table.baseTeamTable:}'.empty ? null : '${tenant-table.baseTeamTable:}'.split(' ')}")
    private List<String> baseTeamTable;

    @Value("#{'${tenant-table.baseProjectTable:}'.empty ? null : '${tenant-table.baseProjectTable:}'.split(' ')}")
    private List<String> baseProjectTable;

    @Value("#{'${tenant-table.baseNoSnowflakeTable:}'.empty ? null : '${tenant-table.baseNoSnowflakeTable:}'.split(' ')}")
    private List<String> baseNoSnowflakeTable;


    //暂时写死
    private String voPackScan = "com.eastfair";
    private static final String LOCAL = "local";
    private static final String DEV = "dev";
    private static final String TEST = "test";
    private static final String PROD = "prod";

    @Value("${spring.profiles.active}")
    private String profilesActive;

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${com.eastfair.systemScene}")
    private String projectSystemScene;

    @Value("${com.eastfair.isAllLogOpen}")
    private Integer isAllLogOpen;

    @Value("#{'${voPackage:}'.empty ? null : '${voPackage:}'.split(' ')}")
    private List<String> packAgeList;


    @Autowired
    private WebApplicationContext applicationContext;

    @Autowired
    private CachedUidGenerator cachedUidGenerator;

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        log.info("ApplicationRunnerCore is execute");
        //VO包扫描
        voScanByAllClass();
        //记录IP地址
        String ip = NetUtils.getLocalAddress();
        StaticContextUtil.setIpAddr(ip);
        ContextUtil.setIpAddr(ip);
        //记录是否是开发环境
        if (DEV.equalsIgnoreCase(profilesActive)) {
            StaticContextUtil.setProfilesActive(DEV);
        } else if (TEST.equalsIgnoreCase(profilesActive)) {
            StaticContextUtil.setProfilesActive(TEST);
        } else if (LOCAL.equalsIgnoreCase(profilesActive)) {
            StaticContextUtil.setProfilesActive(LOCAL);
        } else {
            StaticContextUtil.setProfilesActive(PROD);
        }
        //系统重启时间
        StaticContextUtil.setReLoadTime(System.currentTimeMillis());

        //执行加载雪花id
        executeWorkNode();


        //执行业务代码
        executeBusinessRunner();


    }

    /**
     * VO包扫描
     */
    public void voScanByAllClass() {
        Set<Class<?>> classSet = new HashSet<>();
        for (String packageName : packAgeList) {
            classSet.addAll(ClassUtil.scanPackage(packageName));
        }
        if (classSet != null && classSet.size() > 0) {
            for (Class class2 : classSet) {
                //没有DTO标识也能加载
//                if (DynamicAnno.isVoInfoClass(class2)) {
                FieldTypeUtil.addVoBusinClass(class2);
//                }
            }
        }
        log.info("加载bean 类 {} " + JSON.toJSONString(FieldTypeUtil.getBuinClass()));
    }


    /**
     * 加载雪花id
     */
    public void executeWorkNode() {
        if (!"project-core".equals(applicationName)) {
            WorkerNode workerNodeEntity = buildWorkerNode();
            R<WorkerNode> workerNodeR = projectCoreServiceFeign.createBaiduWorkNode(workerNodeEntity);
            log.info("Add worker node:{}", JSONObject.toJSONString(workerNodeR));
            StaticContextUtil.setStaticProjectWorkId(String.valueOf(workerNodeR.getData().getId()));
            WorkerNode workerNode = workerNodeR.getData();
            WorkerNodeOfUid workerNodeOfUid = new WorkerNodeOfUid();
            BeanUtils.copyProperties(workerNode,workerNodeOfUid);
            workerNodeOfUid.setId(workerNode.getId());
            cachedUidGenerator.initBean(workerNodeOfUid);
        }
    }

    /**
     * Build worker node entity by IP and PORT
     */
    private WorkerNode buildWorkerNode() {
        WorkerNode workerNodeEntity = new WorkerNode();
        if (DockerUtils.isDocker()) {
            workerNodeEntity.setType(WorkerNodeType.CONTAINER.value());
            workerNodeEntity.setHostName(DockerUtils.getDockerHost());
            workerNodeEntity.setPort(DockerUtils.getDockerPort());

        } else {
            workerNodeEntity.setType(WorkerNodeType.ACTUAL.value());
            workerNodeEntity.setHostName(com.eastfair.uid.baidu.util.NetUtils.getLocalInetAddress().getHostAddress());
            workerNodeEntity.setPort(System.currentTimeMillis() + "-" + RANDOM.nextInt(100000));
        }
        workerNodeEntity.setLaunchDate(LocalDate.now());
        workerNodeEntity.setModified(LocalDateTime.now());
        workerNodeEntity.setCreated(LocalDateTime.now());
        return workerNodeEntity;
    }


    /**
     * 执行业务代码
     */
    public void executeBusinessRunner() {
        System.out.println("executeBusiness is execute");
        StaticContextUtil.setApplicationName(applicationName);
        StaticContextUtil.setProjectSystemScene(projectSystemScene);
        if (!"project-core".equals(applicationName)) {
            //每次重启服务创建一次workid
//            Long l = projectCoreServiceFeign.createWorkNode();
//            if (l != null && l != 0) {
//                ContextConstants.WORK_ID = l;
//                StaticContextUtil.setStaticProjectWorkId(String.valueOf(l));
//            }
            //系统数据
            DataIsolationDTO dataIsolationDTO = new DataIsolationDTO();
            R<List<DataIsolationVo>> listR = projectCoreServiceFeign.queryDataIsolaction(dataIsolationDTO);
            if (listR.getIsSuccess()) {
                List<DataIsolationVo> dataIsolationVoList = listR.getData();
                StaticContextUtil.setDataIsolcationList(dataIsolationVoList == null ? null : JSON.toJSONString(dataIsolationVoList));
                //设置本系统数据隔离级别
                List<Map> list = new ArrayList<>();
                for (DataIsolationVo dataIsolationVo : dataIsolationVoList) {
                    if (dataIsolationVo.getSystemScene().equals(projectSystemScene) && StringUtils.isNotBlank(dataIsolationVo.getDataScope())) {
                        Map map = new HashMap();
                        map.put("projectId", dataIsolationVo.getProjectId());
                        map.put("dataScope", dataIsolationVo.getDataScope());
                        map.put("systemId", dataIsolationVo.getId());
                        map.put("businDataScope", dataIsolationVo.getBusinDataScope());
                        map.put("systemType", dataIsolationVo.getSystemType());
                        list.add(map);
                    }
                }

                if (!CollectionUtils.isEmpty(list)) {
                    String systemType = (String) list.get(0).get("systemType");
                    String businDataScope = (String) list.get(0).get("businDataScope");
                    DataIsolationModel.setSelfDataScope(DataScopeEnum.get(businDataScope));
                    DataIsolationModel.setSelfTag(SystemTagEnum.get(systemType));
                }
                StaticContextUtil.setStaticProjectSystemDataScope(list.size() == 0 ? "" : JSON.toJSONString(list));
            }


            //加载租户规则信息
//            R<List<TenantRuleVO>> listR = projectCoreServiceFeign.queryTenantTable();
//            if(SUCCESS_CODE == listR.getCode()){
//                List<TenantRuleVO> tenantRuleVOList = listR.getData();
//                if(tenantRuleVOList !=null && !tenantRuleVOList.isEmpty()){
//                    StaticContextUtil.setTenantRule(JSONObject.toJSONString(tenantRuleVOList));
//                }
//            }

            //执行字典回显缓存操作
            distionaryDynamicAnno.removeData();

            //获取接口地址信息
//            List<JSONObject> jsonObjectList = getIntefaceAllUrl();
//            //添加
//            if (CollectionUtil.isNotEmpty(jsonObjectList)) {
//                List<List<JSONObject>> listList = splistList(jsonObjectList, 50);
//                for (List<JSONObject> jsonObjectList1 : listList) {
//                    projectCoreServiceFeign.executeAllUrl(jsonObjectList1);
//                }
//            }
        }

        //初始化字典
//        dictionaryFeign.initDictData();
        //初始化记录全量日志工具
        MessageAppenderFactory.init(redisOps, isAllLogOpen);
        //加载表的隔离级别
        assmbleTable();
    }


    public static <T> List<List<T>> splistList(List<T> list, int subNum) {
        List<List<T>> tNewList = new ArrayList<List<T>>();
        int priIndex = 0;
        int lastPriIndex = 0;
        int insertTimes = list.size() / subNum;
        List<T> subList = new ArrayList<>();
        for (int i = 0; i <= insertTimes; i++) {
            priIndex = subNum * i;
            lastPriIndex = priIndex + subNum;
            if (i == insertTimes) {
                subList = list.subList(priIndex, list.size());
            } else {
                subList = list.subList(priIndex, lastPriIndex);
            }
            if (subList.size() > 0) {
                tNewList.add(subList);
            }
        }
        return tNewList;
    }


    public void assmbleTable() {
        //不需要多租户的表
        if (CollectionUtil.isNotEmpty(baseUntenant)) {
            TenantIgnoreTable.setBaseTable(baseUntenant);
        }
        if (CollectionUtil.isNotEmpty(baseSystemOrTeamTable)) {
            TenantIgnoreTable.setBaseSystemOrTeamTable(baseSystemOrTeamTable);
        }
        if (CollectionUtil.isNotEmpty(baseSystemTable)) {
            TenantIgnoreTable.setBaseSystemTable(baseSystemTable);
        }
        if (CollectionUtil.isNotEmpty(baseTeamTable)) {
            TenantIgnoreTable.setBaseTeamTable(baseTeamTable);
        }
        if (CollectionUtil.isNotEmpty(baseProjectTable)) {
            TenantIgnoreTable.setBaseProjectTable(baseProjectTable);
        }
        if (CollectionUtil.isNotEmpty(baseNoSnowflakeTable)) {
            TenantIgnoreTable.setBaseNoSnowflakeTable(baseNoSnowflakeTable);
        }
    }


    /**
     * 获取接口地址信息
     *
     * @return
     */
    public List<JSONObject> getIntefaceAllUrl() {
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        // 获取url与类和方法的对应信息
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        List<JSONObject> list = new ArrayList<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : map.entrySet()) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("systemName", projectSystemScene);
            RequestMappingInfo info = m.getKey();
            HandlerMethod method = m.getValue();
            //获取当前方法所在类名
            Class<?> bean = method.getBeanType();
            //使用反射获取当前类注解内容
            Api api = bean.getAnnotation(Api.class);
            if (api != null) {
                try {
                    jsonObject.put("classExplain", api.tags()[0]);
                } catch (Exception e) {

                }
            }
            RequestMapping requestMapping = bean.getAnnotation(RequestMapping.class);
            String[] value = requestMapping.value();
            jsonObject.put("parent", value[0]);
            //获取方法上注解以及注解值
            ApiOperation methodAnnotation = method.getMethodAnnotation(ApiOperation.class);
            if (methodAnnotation != null) {
                String privilegeName = methodAnnotation.value();
                jsonObject.put("explain", privilegeName);
            }
            PatternsRequestCondition p = info.getPatternsCondition();
            for (String url : p.getPatterns()) {
                jsonObject.put("url", url);
            }
            jsonObject.put("className", method.getMethod().getDeclaringClass().getName());
            jsonObject.put("method", method.getMethod().getName());
            RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
            for (RequestMethod requestMethod : methodsCondition.getMethods()) {
                jsonObject.put("methodType", requestMethod.toString());
            }
            jsonObject.put("fullUrl", jsonObject.getString("parent") + jsonObject.getString("url") + ":" + jsonObject.getString("methodType"));
            if (jsonObject.getString("className").indexOf("com.eastfair") > -1) {
                list.add(jsonObject);
            }
        }
        return list;
    }
}
